home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (c) 1980 Regents of the University of California.
- * All rights reserved. The Berkeley software License Agreement
- * specifies the terms and conditions for redistribution.
- */
-
- #ifndef lint
- static char *sccsid = "@(#)printf.c 7.4 (Berkeley) 3/9/87; 1.2 (Bellcore) 87/04/24";
- /* The pwb version this is based on */
- static char *printf_id = "@(#) printf.c:2.2 6/5/79";
- #endif not lint
-
- #include <varargs.h>
-
- /*
- * This version of printf is compatible with the Version 7 C
- * printf. The differences are only minor except that this
- * printf assumes it is to print through putchar. Version 7
- * printf is more general (and is much larger) and includes
- * provisions for floating point.
- */
-
- #define MAXOCT 11 /* Maximum octal digits in a long */
- #define MAXINT 32767 /* largest normal length positive integer */
- #define BIG 1000000000 /* largest power of 10 less than an unsigned long */
- #define MAXDIGS 10 /* number of digits in BIG */
-
- static int width, sign, fill;
-
- char *_p_dconv();
-
- /* VARARGS */
- ex_printf(va_alist)
- va_dcl
- {
- va_list ap;
- register char *fmt;
- char fcode;
- int prec;
- int length,mask1,nbits,n;
- long int mask2, num;
- register char *bptr;
- char *ptr;
- char buf[134];
-
- va_start(ap);
- fmt = va_arg(ap,char *);
- for (;;) {
- /* process format string first */
- while ((fcode = *fmt++)!='%') {
- /* ordinary (non-%) character */
- if (fcode=='\0')
- return;
- ex_putchar(fcode);
- }
- /* length modifier: -1 for h, 1 for l, 0 for none */
- length = 0;
- /* check for a leading - sign */
- sign = 0;
- if (*fmt == '-') {
- sign++;
- fmt++;
- }
- /* a '0' may follow the - sign */
- /* this is the requested fill character */
- fill = 1;
- if (*fmt == '0') {
- fill--;
- fmt++;
- }
-
- /* Now comes a digit string which may be a '*' */
- if (*fmt == '*') {
- width = va_arg(ap, int);
- if (width < 0) {
- width = -width;
- sign = !sign;
- }
- fmt++;
- }
- else {
- width = 0;
- while (*fmt>='0' && *fmt<='9')
- width = width * 10 + (*fmt++ - '0');
- }
-
- /* maybe a decimal point followed by more digits (or '*') */
- if (*fmt=='.') {
- if (*++fmt == '*') {
- prec = va_arg(ap, int);
- fmt++;
- }
- else {
- prec = 0;
- while (*fmt>='0' && *fmt<='9')
- prec = prec * 10 + (*fmt++ - '0');
- }
- }
- else
- prec = -1;
-
- /*
- * At this point, "sign" is nonzero if there was
- * a sign, "fill" is 0 if there was a leading
- * zero and 1 otherwise, "width" and "prec"
- * contain numbers corresponding to the digit
- * strings before and after the decimal point,
- * respectively, and "fmt" addresses the next
- * character after the whole mess. If there was
- * no decimal point, "prec" will be -1.
- */
- switch (*fmt) {
- case 'L':
- case 'l':
- length = 2;
- /* no break!! */
- case 'h':
- case 'H':
- length--;
- fmt++;
- break;
- }
-
- /*
- * At exit from the following switch, we will
- * emit the characters starting at "bptr" and
- * ending at "ptr"-1, unless fcode is '\0'.
- */
- switch (fcode = *fmt++) {
- /* process characters and strings first */
- case 'c':
- buf[0] = va_arg(ap, int);
- ptr = bptr = &buf[0];
- if (buf[0] != '\0')
- ptr++;
- break;
- case 's':
- bptr = va_arg(ap,char *);
- if (bptr==0)
- bptr = "(null pointer)";
- if (prec < 0)
- prec = MAXINT;
- for (n=0; *bptr++ && n < prec; n++) ;
- ptr = --bptr;
- bptr -= n;
- break;
- case 'O':
- length = 1;
- fcode = 'o';
- /* no break */
- case 'o':
- case 'X':
- case 'x':
- if (length > 0)
- num = va_arg(ap,long);
- else
- num = (unsigned)va_arg(ap,int);
- if (fcode=='o') {
- mask1 = 0x7;
- mask2 = 0x1fffffffL;
- nbits = 3;
- }
- else {
- mask1 = 0xf;
- mask2 = 0x0fffffffL;
- nbits = 4;
- }
- n = (num!=0);
- bptr = buf + MAXOCT + 3;
- /* shift and mask for speed */
- do
- if (((int) num & mask1) < 10)
- *--bptr = ((int) num & mask1) + 060;
- else
- *--bptr = ((int) num & mask1) + 0127;
- while (num = (num >> nbits) & mask2);
-
- if (fcode=='o') {
- if (n)
- *--bptr = '0';
- }
- else
- if (!sign && fill <= 0) {
- ex_putchar('0');
- ex_putchar(fcode);
- width -= 2;
- }
- else {
- *--bptr = fcode;
- *--bptr = '0';
- }
- ptr = buf + MAXOCT + 3;
- break;
- case 'D':
- case 'U':
- case 'I':
- length = 1;
- fcode = fcode + 'a' - 'A';
- /* no break */
- case 'd':
- case 'i':
- case 'u':
- if (length > 0)
- num = va_arg(ap,long);
- else {
- n = va_arg(ap,int);
- if (fcode=='u')
- num = (unsigned) n;
- else
- num = (long) n;
- }
- if (n = (fcode != 'u' && num < 0))
- num = -num;
- /* now convert to digits */
- bptr = _p_dconv(num, buf);
- if (n)
- *--bptr = '-';
- if (fill == 0)
- fill = -1;
- ptr = buf + MAXDIGS + 1;
- break;
- default:
- /* not a control character,
- * print it.
- */
- ptr = bptr = &fcode;
- ptr++;
- break;
- }
- if (fcode != '\0')
- _p_emit(bptr,ptr);
- }
- va_end(ap);
- }
-
- /* _p_dconv converts the unsigned long integer "value" to
- * printable decimal and places it in "buffer", right-justified.
- * The value returned is the address of the first non-zero character,
- * or the address of the last character if all are zero.
- * The result is NOT null terminated, and is MAXDIGS characters long,
- * starting at buffer[1] (to allow for insertion of a sign).
- *
- * This program assumes it is running on 2's complement machine
- * with reasonable overflow treatment.
- */
- char *
- _p_dconv(value, buffer)
- long value;
- char *buffer;
- {
- register char *bp;
- register int svalue;
- int n;
- long lval;
-
- bp = buffer;
-
- /* zero is a special case */
- if (value == 0) {
- bp += MAXDIGS;
- *bp = '0';
- return(bp);
- }
-
- /* develop the leading digit of the value in "n" */
- n = 0;
- while (value < 0) {
- value -= BIG; /* will eventually underflow */
- n++;
- }
- while ((lval = value - BIG) >= 0) {
- value = lval;
- n++;
- }
-
- /* stash it in buffer[1] to allow for a sign */
- bp[1] = n + '0';
- /*
- * Now develop the rest of the digits. Since speed counts here,
- * we do it in two loops. The first gets "value" down until it
- * is no larger than MAXINT. The second one uses integer divides
- * rather than long divides to speed it up.
- */
- bp += MAXDIGS + 1;
- while (value > MAXINT) {
- *--bp = (int)(value % 10) + '0';
- value /= 10;
- }
-
- /* cannot lose precision */
- svalue = value;
- while (svalue > 0) {
- *--bp = (svalue % 10) + '0';
- svalue /= 10;
- }
-
- /* fill in intermediate zeroes if needed */
- if (buffer[1] != '0') {
- while (bp > buffer + 2)
- *--bp = '0';
- --bp;
- }
- return(bp);
- }
-
- /*
- * This program sends string "s" to putchar. The character after
- * the end of "s" is given by "send". This allows the size of the
- * field to be computed; it is stored in "alen". "width" contains the
- * user specified length. If width<alen, the width will be taken to
- * be alen. "sign" is zero if the string is to be right-justified
- * in the field, nonzero if it is to be left-justified. "fill" is
- * 0 if the string is to be padded with '0', positive if it is to be
- * padded with ' ', and negative if an initial '-' should appear before
- * any padding in right-justification (to avoid printing "-3" as
- * "000-3" where "-0003" was intended).
- */
- _p_emit(s, send)
- register char *s;
- char *send;
- {
- char cfill;
- register int alen;
- int npad;
-
- alen = send - s;
- if (alen > width)
- width = alen;
- cfill = fill>0? ' ': '0';
-
- /* we may want to print a leading '-' before anything */
- if (*s == '-' && fill < 0) {
- ex_putchar(*s++);
- alen--;
- width--;
- }
- npad = width - alen;
-
- /* emit any leading pad characters */
- if (!sign)
- while (--npad >= 0)
- ex_putchar(cfill);
-
- /* emit the string itself */
- while (--alen >= 0)
- ex_putchar(*s++);
-
- /* emit trailing pad characters */
- if (sign)
- while (--npad >= 0)
- ex_putchar(cfill);
- }
-